package helper.scxml.scxml2.zone.t2.case3_factoryLines

import helper.base.A2LHM
import helper.base.BaseTypeHelper.LHMGetExpand.getMaxKey
import helper.base.LHMHelper.A2LHMExpand.add
import helper.base.LHMHelper.A2LHMExpand.makeA2LHM
import helper.base.debug.DebugHelper.DebuggerList
import helper.base.debug.DebugHelper.DebuggerList.Companion.makeDebuggerList
import helper.base.math.DoubleMatrixHelper.DoubleMatrix
import helper.scxml.scxml2.MathHelper.LocationEventVListLHM
import helper.scxml.scxml2.zone.t2.ConfigHelper.Config
import helper.scxml.scxml2.zone.t2.T3SNodeUnitHelper
import helper.scxml.scxml2.zone.t2.T3SNodeUnitHelper.ERT3SNodeUnit
import helper.scxml.scxml2.zone.t2.ZoneEnvHelper.ZoneEnv
import helper.scxml.scxml2.zone.t2.ZoneLearningHelper.GetDFun
import helper.scxml.scxml2.zone.t2.ZoneLearningHelper.IDsDFun
import helper.scxml.scxml2.zone.t2.ZoneLearningHelper.LearningHelper
import helper.scxml.scxml2.zone.t2.case3_factoryLines.FLEnvHelper.FLEnv.Companion.aPMs
import helper.scxml.scxml2.zone.t2.case3_factoryLines.FLEnvHelper.FLEnv.Companion.makeFLEnv
import helper.scxml.scxml2.zone.t2.case3_factoryLines.FLEnvHelper.FLT3StrategyEx
import helper.scxml.scxml2.zone.t2.case3_factoryLines.FLEnvHelper.FLT3StrategyEx.Companion.makeFLT3StrategyEx
import helper.scxml.scxml2.zone.t2.case3_factoryLines.FunArgHelperKt.ZoneStateStringFunArgHelper.aForMachineGetZoneStateFunction
import helper.scxml.strategy.StrategyHelper.TimedEventHelper.TimedEvent
import helper.scxml.strategy.StrategyHelper.TimedEventHelper.TimedEvent.Companion.makeTimedEvent
import helper.scxml.strategy.TotalStrategyHelper.LeafSNode.Companion.makeLeafSNode
import helper.scxml.strategy.TotalStrategyHelper.SNode
import helper.scxml.strategy.sNodeUnit.SNodeUnitHelper.ISNodeUnit
import helper.scxml.strategy.sNodeUnit.T2SNodeUnitHelper.SubT2SNodeUnit
import kotlin.properties.Delegates

object FLLearningHelperKt {
    class FLLearningHelper : LearningHelper() {
        companion object {
            fun makeFLLearningHelper(
                maxRuns_repeatNum: Int = 0,
                maxGood_repeatNum: Int = 0,
                maxBest_heapSize: Int = 0,
                maxNoBetter: Int = 0,
                maxIterations: Int = 100,
                maxResets: Int = Int.MAX_VALUE,
                getDFun: GetDFun,
                clearHeapP: Double,
                ifToTxt: Boolean,
                doDoubleMatrixFun: DoubleMatrix.() -> DoubleMatrix,
                aDsDFun: IDsDFun,
                timeNeedEnd: Int,
            ): FLLearningHelper {
                return FLLearningHelper().also {
                    it.maxRuns_repeatNum = maxRuns_repeatNum
                    it.maxGood_repeatNum = maxGood_repeatNum
                    it.maxBest_heapSize = maxBest_heapSize
                    it.maxNoBetter = maxNoBetter
                    it.maxIterations = maxIterations
                    it.maxResets = maxResets
                    it.getDFun = getDFun
                    it.clearHeapP = clearHeapP
                    it.ifToTxt = ifToTxt
                    it.doDoubleMatrixFun = doDoubleMatrixFun
                    it.aDsDFun = aDsDFun
                    it.timeNeedEnd = timeNeedEnd
                }
            }

            val aGetTotalScoreFun = { aDsDFun: IDsDFun, cal: A2LHM<String, Double> ->
                aDsDFun.aDsToString(
                    cal["t"]!!,
                    cal["A1C1"]!!,
                    cal["B1C2"]!!,
                    cal["A2D1"]!!,
                    cal["B2D2"]!!,
                )
            }
        }

        override fun toL2String(): String {
            val sb = StringBuilder()
            sb.append("T2ArgUnit(\n")
            sb.append("\tmaxRuns_repeatNum=$maxRuns_repeatNum\n")
            sb.append("\tmaxGood_repeatNum=$maxGood_repeatNum\n")
            sb.append("\tmaxBest_heapSize=$maxBest_heapSize\n")
            sb.append("\tmaxNoBetter=$maxNoBetter\n")
            sb.append("\tmaxIterations=$maxIterations\n")
            sb.append("\tmaxResets=$maxResets\n")
            sb.append("\tgetDFun=${getDFun}\n")
            sb.append("\tclearHeapP=$clearHeapP\n")
            sb.append("\tifToTxt=$ifToTxt\n")
            sb.append("\tdoDoubleMatrixFun=${doDoubleMatrixFun}\n")
            sb.append("\taDsDFun=${aDsDFun}\n")
            sb.append("\ttimeNeedEnd=${timeNeedEnd}\n")
            sb.append(")")
            return sb.toString()
        }

        override fun getTotalScore(cal: A2LHM<String, Double>): Double {
            return aGetTotalScoreFun(aDsDFun, cal)
        }

        override val aEnvLabel = "3_fl"

        var timeNeedEnd: Int by Delegates.notNull()

        override val aGetEnvFun: () -> ZoneEnv by lazy {
            {
                makeFLEnv(timeNeedEnd)
            }
        }

        override fun getEnvStrategyCal(
            aEnv: ZoneEnv,
            aStrategy: ISNodeUnit,
            debuggerList: DebuggerList,
        ): A2LHM<String, Double> {
            return aEnv.aT0Calculate(
                aSNU = aStrategy,
                repeatTimes = 1,
                debuggerList = debuggerList,
            )
        }

        override fun byWeightSelectChoice(
            env: ZoneEnv,
            ren: SubT2SNodeUnit,
            locationName: String,
            locationEventVListLHM: LocationEventVListLHM,
            zs: Config,
            aSNode: SNode,
            nowTString: String,
            nowDoubleT: Double,
        ) {
            val choiceWeightLHM = makeA2LHM<TimedEvent, Double>()
            val newV = arrayListOf<Double>()
            getAboutVarList(env).forEach {
                zs.byNameFindC(it).getPointD().let {
                    newV.add(it)
                }
            }
            locationEventVListLHM[locationName]?.forEach { (event, v) ->
                val weight = v.getWeightOf(newV, getDFun.getDFun)
                choiceWeightLHM.add(
                    makeTimedEvent(
                        env.stateStayMaxLHM[locationName]!!,
                        event,
                    ),
                    weight,
                )
            }
            choiceWeightLHM.getMaxKey()!!.let { maxWeightTimeEventChoice: TimedEvent ->
                val leafSNode = makeLeafSNode()
                leafSNode.eventDPLHM.add(maxWeightTimeEventChoice, 1.0)
                ren.relationLHM.add(aSNode, nowTString, leafSNode)
            }
        }

        fun aT1ByLearningMakeStrategy(
            aDirStr: String = aBaseDirStr,
            aFLT3StrategyEx: FLT3StrategyEx = makeFLT3StrategyEx(),
            getUStrategyScoreFun: () -> Double = { Double.MAX_VALUE },
            getBestStrategyScoreFun: () -> Double = { Double.MAX_VALUE },
            debuggerList: DebuggerList = makeDebuggerList(),
        ): ERT3SNodeUnit {
            return fByLearningMakeStrategy(
                aDirStr,
                aFLT3StrategyEx,
                { env, locationEventVListLHM, aStrategyEx ->
                    val aFLT3SNodeUnit = aStrategyEx.makeNewStrategy()
                    aPMs.forEach { aM ->
                        val sMiName = aM.name
                        val ren = aFLT3SNodeUnit.aMsERT2SNodeUnitLHM[sMiName]!!.ren
                        ren.funLHM.add(
                            null,
                            aForMachineGetZoneStateFunction(sMiName),
                        )
                        arrayOf("${sMiName}0").forEach { locationName ->
                            doWhenEnvRenLocation(
                                env,
                                ren,
                                locationName,
                                locationEventVListLHM,
                            )
                        }
                    }
                    aFLT3SNodeUnit
                },
                getUStrategyScoreFun = getUStrategyScoreFun,
                getBestStrategyScoreFun = getBestStrategyScoreFun,
                debuggerList = debuggerList,
            )
        }

        fun repeatT1ByLearningMakeStrategy(
            repeatTimes: Int,
            aBaseDirStr: String = this.aBaseDirStr,
            shouldDrawChart: Boolean = true,
            debuggerList: DebuggerList = makeDebuggerList(),
        ) {
            fRepeatT1ByLearningMakeStrategy(
                repeatTimes,
                { aDirStr ->
                    aT1ByLearningMakeStrategy(
                        aDirStr,
                        debuggerList = debuggerList,
                    )
                },
                aBaseDirStr,
                shouldDrawChart,
                debuggerList,
            )
        }
    }
}